from expression import expression, environment
from customExceptions import *
import sys
sys.path.append(sys.path[0] + "/../LLVM")
from commonRoutines import *
from llvm import *

closureID = 0

class jsobject(expression):
	def __init__(self):
		self.slots = environment("",None)
	
	def access(self, name):
		#print "slots: ", str(self.slots)
		return self.slots.getVarRef(name)
	
	def mutate(self, name, value):
		try:
			self.slots.mutateVarRef(name, value)
		except VarNotFoundExcpt:
			self.slots = environment(name, value, self.slots)
		return value
	
	def evaluate(self, env):
		return self
		
	def __str__(self):
		return "Object: " + str(self.slots)

	def traverse(self, helper):
		pass


	def toLLVM(self, helper):
		helper.descend("jsobject")
		c = commonRoutines(helper)
		

		#Registers
		Reg1 = helper.getNextReg()
		Reg2 = helper.getNextReg()
		Reg3 = helper.getNextReg()
		Reg4 = helper.getNextReg()
		Reg5 = helper.getNextReg()

		helper.addLLVMInst(llvm_malloc(Reg1, "%jsobject"))
		helper.addLLVMInst(llvm_gep(Reg2,Reg1,"%jsobject*",  0 , "i32" , 0 , "i32"), "Get the pointer to the first spot")
		helper.addLLVMInst(llvm_store("0", Reg2), "Save the object type as 0")
		helper.addLLVMInst(llvm_gep(Reg3,Reg1,"%jsobject*",  0 , "i32" , 1 , "i32"), "Access the slots")
		helper.addLLVMInst(llvm_store("@empty_slots", Reg3, "%slots*","%slots*"), "Storing empty slots into the slots slot")
		helper.addLLVMInst(llvm_ptrtoint(Reg4, Reg1, "%jsobject*", "i32"), "Making the object pointer into an int")
		helper.addLLVMInst(llvm_add(Reg5, Reg4, 1, "i32"), "Storing the pointer tag bit by adding one")
		helper.ascend(Reg5)

					
class closure(jsobject):
	def __init__(self, params, body, cloEnv, functionnum):
		jsobject.__init__(self)
		self.params = []
		self.params = params
		self.body = body
		self.cloEnv = cloEnv
		self.functionnum = functionnum

	
	def evaluate(self, env):
		#print "Params: ", slef.params, "ENV: ",env," OVER;"
		self.body.evaluate(env)
	
	def getEnv(self):
		return self.cloEnv
		
	def setEnv(self, env):
		self.cloEnv = env
	
	def getParams(self):
		return self.params
	
	def __str__(self):
		return "Closure:Params: "+str(self.params) 

	def traverse(self, helper):
		self.body.traverse(helper)

#	def toLLVM(self, helper):
#		helper.descend("closure")
#		c = commonRoutines(helper)
		

		#Registers
#		Reg1 = helper.getNextReg()
#		Reg2 = helper.getNextReg()
#		Reg3 = helper.getNextReg()
#		Reg4 = helper.getNextReg()
#		Reg5 = helper.getNextReg()
#		currentScopeReg = helper.getCurrentScopeReg()
#		helper.addLLVMInst(llvm_malloc(Reg1, "%closure"))
#		helper.addLLVMInst(llvm_gep(Reg2,Reg1,"%closure*",  0 , "i32" , 0 , "i32"), "Get the pointer to the first spot")
#		helper.addLLVMInst(llvm_store("5", Reg2), "Save the object type as 5 for closures")
#		helper.addLLVMInst(llvm_gep(Reg3,Reg1,"%closure*",  0 , "i32" , 1 , "i32"), "Access the slot for function number")
#		helper.addLLVMInst(llvm_store(self.functionnum, Reg3), "Storing the function number in the second slot")
#		helper.addLLVMInst(llvm_gep(Reg4,Reg1,"%closure*",  0 , "i32" , 2 , "i32"), "Access the slot for current eframe")
#		helper.addLLVMInst(llvm_store(currentScopeReg, Reg4), "Storing the function number in the second slot")
#		helper.addLLVMInst(llvm_ptrtoint(Reg4, Reg1, "%closure*", "i32"), "Making the object pointer into an int")
#		helper.addLLVMInst(llvm_add(Reg5, Reg4, 1, "i32"), "Storing the pointer tag bit by adding one")
#		helper.ascend(Reg5)

class stringval(jsobject):
	def __init__(self, string):
		jsobject.__init__(self)
		self.string = string
		
	def evaluate(self, env):
		return self
		
	def getString(self):
		return self.string
		
	def __str__(self):
		return self.string

	def traverse(self, helper):
		pass
		
	def __add__(self, other):
		if(isinstance(other, stringval)):
			self.string = self.getString() + other.getString()
			return self
		else:
			raise GeneralException("Tried to add non-stringval to a stringval")
	def toLLVM(self, helper):
		helper.descend("stringval")
		c = commonRoutines(helper)
		strName = helper.addStringVal(self.string)
		if(strName == None):
			st = helper.getSavedString(self.string)	
			helper.ascend(st)
			return
		strlen = len(self.string) + 1

		argValues = helper.getReturns()

		#Labels
#		stringval = helper.getNextLabel()
#		endlabel = helper.getNextLabel()

		#Registers
		Reg1 = helper.getNextReg()
		Reg2 = helper.getNextReg()
		Reg3 = helper.getNextReg()
		Reg4 = helper.getNextReg()
		Reg5 = helper.getNextReg()
		Reg6 = helper.getNextReg()	
		Reg7 = helper.getNextReg()	
		targetReg = helper.getNextReg()
	
		helper.addLLVMLine(Reg1 + " = malloc %strobj, align 4","Create the string obj")
		helper.addLLVMLine(Reg2 + " = getelementptr %strobj* "+Reg1+", i32 0, i32 0")
		helper.addLLVMInst(llvm_store("2", Reg2))
		helper.addLLVMLine(Reg3 + " = getelementptr %strobj* "+Reg1+", i32 0, i32 1")
		helper.addLLVMInst(llvm_store("@empty_slots", Reg3, "%slots*","%slots*"), "Storing empty slots into the slots slot")
		helper.addLLVMLine(Reg4 + " = getelementptr [" + str(strlen) + " x i8]* " + strName + ", i32 0, i32 0")
		helper.addLLVMLine(Reg5 + " = getelementptr %strobj* " + Reg1 + ", i32 0, i32 2")
		helper.addLLVMInst(llvm_store(Reg4, Reg5, "i8*", "i8*"), "Storing the pointer to the string val")
		helper.addLLVMInst(llvm_ptrtoint(Reg6, Reg1, "%strobj*", "i32"), "Making the string pointer into an int")
		helper.addLLVMInst(llvm_add(Reg7, Reg6, 1, "i32"), "Storing the pointer tag bit by adding one")
		helper.addSavedString(self.string, Reg7)

		#dont know
		#helper.addLLVMLine(targetReg + " = add i32 " + argValues[0] + ", " + argValues[1])#add
		#helper.setLabel(endlabel)#last label used, needed for phi instructions
		helper.ascend(Reg7)# last register used, stores result of operation
		

	#def __str__(self):
	#	return "Stringval("+ self.string +"): " + str(self.slots)
		
# applies closures
class new(expression):
	def __init__(self, funct, args):
		global closureID
		self.funct = funct
		self.args = args
		self.closureid = closureID
		closureID += 1
		self.eframe = None
		
	#this may all be wrong		
	def evaluate(self, env):
		constructor =  self.funct.evaluate(env)
		params = constructor.getParams()
		
		
		if(isinstance(constructor, closure)): # function does return a closure
			o = jsobject()
			o.mutate("constructor", constructor)
			params = constructor.getParams()
			#print "Params = ",params, "con: ",constructor
			#append arguments, with correct bindings, to new enviroment
			#call function body

			if(len(self.args) != len(params)):
				raise GeneralException("New call with Function " + str(self.name) + " Has " + str(len(self.args)) + " args expected "+ str(len(params)))
			else:
				newEnv = constructor.getEnv()
				for arg, param in zip(self.args, params):
					#print "arg: " , arg,"param: ",param
					# I think this line should do this:
					# for each parameter in the function, add a variable('x') to the envrionment
					# 'x' has the value of the associated argument, passed in the application
					# In addition, the new environment ('newEnv') is updated as params are added
					# but the old env ('env') is not updated - not that all the argument evaluations
					# use 'env'. I think this is the way to do it.
#					print "arg: ", arg, ", param: ", param
					if(arg != None and param != None):
						newEnv = environment(param, arg.evaluate(env), newEnv)

				newEnv = environment("this", o, newEnv)
				#print "ENV: ",newEnv," OVER;"
			
				try:
					#print "Before: ", str(constructor)
					constructor.evaluate(newEnv)
					#print "after: ", str(constructor)
					return o
				except ReturnExcpt, ret:
					return o
		
	def __eq__(self, other):
		if(isinstance(other, closure)):
			return self.closureid == other.closureid
		else:
			return False
		
	def __str__(self):
		retval = "new( " + str(self.funct) + ", ["
	   	for p in self.args:
			retval += str(p) + ", "	
		retval += "] ID:" + str(self.closureid) + ")"
		return retval

	def traverse(self, helper):
		scope = helper.getCurrentScope()
#		sys.stderr.write("\nvarRef: scope: " + str(scope) +"\n")
#		sys.stderr.write("\nvarRef: funct: " + str(self.funct) +"\n")
		var = helper.getVar(self.funct.name)
		#sys.stderr.write("\nSDFASDFASDFASDF varRef: self.eframe in NEW: " + str(var) + str(self.funct)+ "\n")
		self.eframe = var
		for arg in self.args:
			if(arg != None):
				arg.traverse(helper)

	def toLLVM(self, helper):
		helper.descend("new")
		for a in self.args:
			a.toLLVM(helper)
		
		c = commonRoutines(helper)

		#Registers
		malloced = helper.getNextReg()
		idReg = helper.getNextReg()
		slotsReg = helper.getNextReg()
		eframeReg = helper.getNextReg()
		eframepoint = helper.getNextReg()
		scopeReg = helper.getCurrentScopeReg()
		closureReg = None
		newSlots = helper.getNextReg()
		nameReg = helper.getNextReg()
		valReg = helper.getNextReg()
		nextSlots = helper.getNextReg()
		
		
		returnReg = helper.getNextReg()
		endReg = helper.getNextReg()

		helper.addLLVMInst(llvm_malloc(malloced, "%jsobject"))
		helper.addLLVMInst(llvm_gep(idReg,malloced,"%jsobject*",  0 , "i32" , 0 , "i32"), "Get the pointer to the first spot")
		helper.addLLVMInst(llvm_store("0", idReg), "Save the object type as  0 for jsobject")

		#scopePtr = c.testAndUnmaskPointer(scopeReg, "%eframe*")
		#helper.addLLVMInst(llvm_load(eframeReg, scopePtr, "i32"), "Get the pointer to the current eframe")
		#helper.addLLVMInst(llvm_bitcast(eframepoint, "i32*", eframeReg, "eframe*"), "Change it to an eframe*")
		#loop until you find the closure

		#sys.stderr.write("varRef called in New: self.eframe: " + str(self.eframe) +"\n")
		if(self.eframe != None):
			closureReg = c.fetchVar(self.eframe[1], self.eframe[2])
			
        #if(self.eframe != None):
 	    #	closureReg = (c.fetchVar(self.eframe[1], self.eframe[2]))
        #else:
        #	helper.ascend(helper.checkCurrentFunctionForArg(self.name))

		#ptr = c.testAndUnmaskPointer(closureReg, "%closure*", True)
		
		temp2 = helper.getNextReg()
		helper.addLLVMInst(llvm_ptrtoint(temp2, malloced, "%jsobject*", "i32"))
		helper.debugPrint(temp2, "b")
					 
		helper.addLLVMInst(llvm_gep(slotsReg,malloced,"%jsobject*",  0 , "i32" , 1 , "i32"), "Access the slots")
		helper.addLLVMInst(llvm_malloc(newSlots, "%slots"), "Creating the slots object")
		
		temp = helper.getNextReg()
		helper.addLLVMInst(llvm_ptrtoint(temp, newSlots, "%slots*", "i32"))
		helper.debugPrint(temp, "a")
		
		helper.addLLVMInst(llvm_gep(nameReg,newSlots,"%slots*",  0 , "i32" , 0 , "i32"), "Get the pointer to the first spot")
		helper.addLLVMInst(llvm_store("0", nameReg), "constructor is 0!")
		helper.addLLVMInst(llvm_gep(valReg,newSlots,"%slots*",  0 , "i32" , 1 , "i32"), "Get the pointer to the identifier spot")
		helper.addLLVMInst(llvm_store(closureReg, valReg), "Add the closure to the slots")
		helper.addLLVMInst(llvm_gep(nextSlots,newSlots,"%slots*",  0 , "i32" , 2 , "i32"), "Get the pointer to the next slot")
		helper.addLLVMInst(llvm_store("@empty_slots", nextSlots, "%slots*", "%slots*"), "add empty slots to the end")
		helper.addLLVMInst(llvm_store(newSlots, slotsReg,"%slots*", "%slots*"), "Placing slots in JS object")
			

		mallocReg = helper.getNextReg()
		argArrayReg = helper.getNextReg()
		pointerToNumReg = helper.getNextReg()
		resultReg = helper.getNextReg()
		
		helper.addLLVMInst(llvm_malloc(mallocReg, "{i32, [ " + str(len(self.args)) + " x i32 ]}"), "Creating array for args")
		helper.addLLVMInst(llvm_bitcast(argArrayReg, mallocReg, "{i32, [ " + str(len(self.args)) + " x i32 ]}*", "%valArray*"), "Bitcasting to the right type")
		helper.addLLVMInst(llvm_gep(pointerToNumReg, argArrayReg, "%valArray*", 0, "i32", 0), "Get location of array size")
		helper.addLLVMInst(llvm_store(len(self.args), pointerToNumReg), "Thow in number (not shifted!)")
	
		argValues = helper.getReturns()
		#sys.stderr.write("Arg values: " + str(argValues) + "\n" )	
		#sys.stderr.write("\nnew self.args: " + str(self.args) +"\n")
		
		for a in range(len(self.args)):
			#sys.stderr.write("\nthis arg: " + str(a) +":" + str(argValues[a]) + "\n")
			argAddr = helper.getNextReg()
			
			helper.addLLVMInst(llvm_gep(argAddr, argArrayReg, "%valArray*", 0, "i32", 1, "i32", a), "Get location of " + str(a) + "th argument")
			helper.addLLVMInst(llvm_store(argValues[a], argAddr), "Good god, I hope those are I32s. Adding arg")

		helper.addLLVMInst(llvm_ptrtoint(returnReg, malloced, "%jsobject*", "i32"), "Making the object pointer into an int")
		helper.addLLVMInst(llvm_add(endReg, returnReg, 1, "i32"), "Storing the pointer tag bit by adding one")		
		
		helper.addLLVMInst(llvm_call("method_dispatch", "i32", resultReg, closureReg, "i32", endReg, "i32", argArrayReg, "%valArray*"), "Call Dispatch!")
		


		helper.setLabel(helper.getLastLabel())
		helper.ascend(endReg)




		
